package com.karrim.server.util;

import com.google.common.base.Optional;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.karrim.server.module.ServerModule;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.lang.SystemUtils;

/**
 *
 * @author andrea.cantafio
 */
public class Util {

    public String decodeModifier(int modifier) {
        if (Modifier.isAbstract(modifier)) {
            return "abstract";
        } else if (Modifier.isFinal(modifier)) {
            return "final";
        } else if (Modifier.isInterface(modifier)) {
            return "interface";
        } else if (Modifier.isNative(modifier)) {
            return "native";
        } else if (Modifier.isPrivate(modifier)) {
            return "private";
        } else if (Modifier.isProtected(modifier)) {
            return "protected";
        } else if (Modifier.isPublic(modifier)) {
            return "public";
        } else if (Modifier.isStatic(modifier)) {
            return "static";
        } else if (Modifier.isStrict(modifier)) {
            return "strict";
        } else if (Modifier.isSynchronized(modifier)) {
            return "synchronized";
        } else if (Modifier.isTransient(modifier)) {
            return "transient";
        } else if (Modifier.isVolatile(modifier)) {
            return "volatile";
        }
        throw new IllegalArgumentException(modifier + " is not a valid modifier.");

    }

    public static Util get() {
        return self;
    }

    public String getClassModel(String packageName, int[] modifiers, String className, Field[] fields) throws IllegalArgumentException {
        return getClassModel(packageName, modifiers, className, fields, true, true);
    }

    public String getClassModel(String packageName, int[] modifiers, String className, Field[] fields, boolean getters, boolean setters) throws IllegalArgumentException {
        return getClassModel(packageName, new String[]{}, modifiers, className, fields, getters, setters);
    }

    public String getClassModel(String packageName, String[] imports, int[] modifiers, String className, Field[] fields, boolean getters, boolean setters) {
        return getClassModel(packageName, imports, modifiers, className, fields, getters, setters, null, null);
    }

    public String getClassModel(String packageName, String[] imports, int[] modifiers, String className, Field[] fields, boolean getters, boolean setters, Optional<Class<?>> superClass, Optional<Class[]> implementedInterfaces) throws IllegalArgumentException {
        if (className.isEmpty()) {
            throw new IllegalArgumentException("className can't be empty.");
        }
        /* Building imports */
        StringBuffer strImports = new StringBuffer("");
        for (String s : imports) {
            strImports.append("import ").append(s).append(";\n");
        }
        strImports.append("\n");
        /* Building fields */
        StringBuffer strFields = new StringBuffer("");
        StringBuffer strGetters = new StringBuffer("");
        StringBuffer strSetters = new StringBuffer("");
        for (Field field : fields) {
            String fieldType = field.getClass().getCanonicalName();
            String fieldName = field.getName();
            strFields.append(decodeModifier(field.getModifiers())).append(" ").append(field.getClass().getCanonicalName()).append(" ").append(fieldType).append(";\n");
            /* Building getters */
            if (getters) {
                strGetters.append("public ").append(fieldType).append(" get").append(Character.toUpperCase(fieldName.charAt(0))).append(fieldName.substring(1, fieldName.length())).append(" () {\n")
                        .append("\treturn ").append(fieldName).append("\n")
                        .append("}\n");
            }
            /* Building setters */
            if (setters) {
                strGetters.append("public ").append(fieldType).append(" set").append(Character.toUpperCase(fieldName.charAt(0))).append(fieldName.substring(1, fieldName.length())).append(" (").append(fieldType).append(" value) {\n")
                        .append("\treturn ").append(fieldName).append("\n")
                        .append("}\n");
            }
        }
        /* Building list of implemented interfaces */
        StringBuffer strImplements = new StringBuffer("");
        if (implementedInterfaces.isPresent()) {
            Class<?>[] implemented = implementedInterfaces.get();
            String delimiter = ", ";
            for (Class<?> c : implemented) {
                strImplements.append(c.getCanonicalName()).append(delimiter);
            }
            strImplements = (StringBuffer) strImplements.subSequence(0, strImplements.length() - delimiter.length());
        }
        /* Building class' modifiers */
        StringBuffer strModifiers = new StringBuffer("");
        boolean isInterface = false;
        for (int i : modifiers) {
            if (Modifier.isInterface(i)) {
                isInterface = true;
            }
            strModifiers.append(decodeModifier(i)).append(" ");
        }

        /* Assembling */
        StringBuffer model = new StringBuffer();
        model
                .append("package ").append(packageName).append(";\n\n")
                .append(strImports).append("\n")
                .append(strModifiers).append(isInterface ? " " : " class ").append(className).append(superClass.isPresent() ? " extends " + superClass.get().getCanonicalName() : "").append(implementedInterfaces.isPresent() ? " implements " : "").append(implementedInterfaces).append(" {\n")
                .append("\t").append(strFields).append("\n")
                .append("\t").append(strGetters).append("\n")
                .append("\t").append(strSetters).append("\n")
                .append("}");
        return model.toString();
    }

    public Injector getInjector(Class<? extends AbstractModule> module) {
        Injector injector = injectors.get(module);
        if (injector == null) {
            try {
                injector = Guice.createInjector((AbstractModule) module.newInstance());
                injectors.put(module, injector);
            } catch (InstantiationException | IllegalAccessException ex) {
                Logger.getLogger(Util.class.getName()).log(Level.SEVERE, null, ex);
                return null;
            }
        }
        return injector;
    }

    /**
     * Returns a sanitized {@link java.lang.String}
     *
     * @param str the {@link java.lang.String} that will be sanitized
     * @return a sanitized {@link java.lang.String}
     */
    public static String sanitize(String str) {
        for (Map.Entry<String, String> entry : evilStrings.entrySet()) {
            str = str.replace(entry.getKey(), entry.getValue());
        }
        return str;
    }
    public final static char FILE_DELIMITER = SystemUtils.IS_OS_WINDOWS ? '\\' : '/';
    /* Relative paths */
    public static final String REL_PATH_SOURCES = "\\src\\main",
            REL_PATH_CLASSES = "\\target\\curve-0.1\\WEB-INF\\classes",
            REL_PATH_RESOURCES = REL_PATH_SOURCES + "\\resources",
            REL_PATH_SOURCE_JAVA = REL_PATH_SOURCES + "\\java";
    /* Absolute paths */
    public static final String ABS_PATH = new File("").getAbsolutePath(),
            ABS_PATH_SOURCES = ABS_PATH + REL_PATH_SOURCES,
            ABS_PATH_CLASSES = ABS_PATH + REL_PATH_CLASSES,
            ABS_PATH_RESOURCES = ABS_PATH + REL_PATH_RESOURCES,
            ABS_PATH_SOURCE_JAVA = ABS_PATH + REL_PATH_SOURCE_JAVA;
    public final static String UNSUPPORTED_OPERATION_EXCEPTION_MESSAGE = "Not supported yet. Maybe never.";
    private final static HashMap<String, String> evilStrings = new HashMap<>();
    /* Initialization of evilStrings Map */

    static {
        evilStrings.put("#", "num");
        evilStrings.put("@", "a");
        evilStrings.put("-", "_");
        evilStrings.put("?", "_");
        evilStrings.put("*", "_");
    }
    private final Map<Class<?>, Injector> injectors = new HashMap<>();
    private final static Util self = new Util();
}
